# -*- coding: utf-8 -*-
#!/usr/bin/env python

import random

from google.appengine.ext import db
from datetime import datetime, timedelta
from utils import *
from app_global import *
from models import *
		
class StatisticsShard(db.Model):
	"""Shards for each named counter"""
	name = db.StringProperty(required=True)
	stamp = db.IntegerProperty(required=True)
	count = db.IntegerProperty(required=True, default=0)

class RatioStatistics(ndb.Model):
	stamp = ndb.IntegerProperty(required=True)
	user_games=ndb.IntegerProperty(repeated=True)
	user_count=ndb.IntegerProperty(default=0)
	step=ndb.IntegerProperty(default=0)

class UsedItem(ndb.Model):
	item0=ndb.IntegerProperty(default=0)
	item1=ndb.IntegerProperty(default=0)
	item2=ndb.IntegerProperty(default=0)
	item3=ndb.IntegerProperty(default=0)
	
class Statistics(ndb.Model):
	stamp = ndb.IntegerProperty(required=True)
	i_register=ndb.IntegerProperty(default=0)
	p_register=ndb.IntegerProperty(default=0)
	i_login=ndb.IntegerProperty(default=0)
	p_login=ndb.IntegerProperty(default=0)
	i_dau=ndb.IntegerProperty(default=0)
	p_dau=ndb.IntegerProperty(default=0)
	i_buy_cashes=ndb.IntegerProperty(repeated=True) # 1~5
	p_buy_cashes=ndb.IntegerProperty(repeated=True)
	i_present_cashes=ndb.IntegerProperty(repeated=True) # 1~5
	p_present_cashes=ndb.IntegerProperty(repeated=True)
	i_buy_coins=ndb.IntegerProperty(repeated=True) # 1~5
	p_buy_coins=ndb.IntegerProperty(repeated=True)
	i_buy_bouts=ndb.IntegerProperty(repeated=True) # 1~5
	p_buy_bouts=ndb.IntegerProperty(repeated=True)
	i_boast=ndb.IntegerProperty(default=0)
	p_boast=ndb.IntegerProperty(default=0)
	i_send_bout=ndb.IntegerProperty(default=0)
	p_send_bout=ndb.IntegerProperty(default=0)
	i_req_bout=ndb.IntegerProperty(default=0)
	p_req_bout=ndb.IntegerProperty(default=0)
	i_invite=ndb.IntegerProperty(default=0)
	p_invite=ndb.IntegerProperty(default=0)
	i_buy_chars=ndb.IntegerProperty(repeated=True) # 1~8 # 추가가능
	p_buy_chars=ndb.IntegerProperty(repeated=True)
	i_adopt_chars=ndb.IntegerProperty(repeated=True) # 1~8 # 추가가능
	p_adopt_chars=ndb.IntegerProperty(repeated=True)
	i_feed_self_chars=ndb.IntegerProperty(repeated=True) # 1~8 # 추가가능
	p_feed_self_chars=ndb.IntegerProperty(repeated=True)
	i_feed_friend_chars=ndb.IntegerProperty(repeated=True) # 1~8 # 추가가능
	p_feed_friend_chars=ndb.IntegerProperty(repeated=True)
	i_buff=ndb.IntegerProperty(repeated=True) # 1~8 # 추가가능
	p_buff=ndb.IntegerProperty(repeated=True)
	i_clear_cooltime=ndb.IntegerProperty(default=0)
	p_clear_cooltime=ndb.IntegerProperty(default=0)
	i_gacha_item=ndb.IntegerProperty(default=0)
	p_gacha_item=ndb.IntegerProperty(default=0)
	i_use_dpoint=ndb.IntegerProperty(default=0) # 단위 100
	p_use_dpoint=ndb.IntegerProperty(default=0)
	i_use_items=ndb.StructuredProperty(UsedItem, repeated=True) # 0~4, 추가가능
	p_use_items=ndb.StructuredProperty(UsedItem, repeated=True)
	i_game_counts=ndb.IntegerProperty(repeated=True) # 0~4, 추가가능
	p_game_counts=ndb.IntegerProperty(repeated=True)
	i_continue=ndb.IntegerProperty(default=0)
	p_continue=ndb.IntegerProperty(default=0)
	i_unregister=ndb.IntegerProperty(default=0)
	p_unregister=ndb.IntegerProperty(default=0)
	i_review=ndb.IntegerProperty(default=0)
	p_review=ndb.IntegerProperty(default=0)
	i_rewards=ndb.IntegerProperty(repeated=True) # 0~3
	p_rewards=ndb.IntegerProperty(repeated=True)
	step=ndb.IntegerProperty(default=0)
	
def getCount(name, stamp):
	qry = StatisticsShard.all().filter('name = ', name).filter('stamp =', stamp)
	return sum([obj.count for obj in qry.run()])

def getRangeCount(name, start, end):
	start = int(start.strftime('%Y%m%d%H'))
	end = int(end.strftime('%Y%m%d%H'))
	
	qry = StatisticsShard.all().filter('name = ', name).filter('stamp >=', start)
	
	total = 0
	objects = {}
	for obj in qry.run(read_policy=db.EVENTUAL_CONSISTENCY):
		if obj.stamp>end:
			break
		if objects.has_key(obj.stamp):
			objects[obj.stamp] += obj.count
		else:
			objects[obj.stamp] = obj.count
		total += obj.count
	
	keys = objects.keys()
	
	counters = []
	
	for key in range(start, end+1):
		try:
			d = replace_timezone(datetime.strptime(str(key), "%Y%m%d%H"),'Asia/Seoul')
		except:
			continue
		stamp = d.strftime('%Y-%m-%d %H:%M:%S')
		if key in keys:
			counters.append({"stamp":stamp, "count":objects[key]})
		else:
			counters.append({"stamp":stamp, "count":0})
	
	return {"counters":counters, "total":total, "name":name}

def increment(name, count=None):
	stamp = int(datetime.now().strftime('%Y%m%d%H'))
	
	def txn():
		index = random.randint(0, 99)
		shard_name = name + str(stamp) + str(index)
		counter = StatisticsShard.get_by_key_name(shard_name)
		if counter is None:
			counter = StatisticsShard(key_name=shard_name, name=name, stamp=stamp)
		counter.count += (count if count else 1)
		counter.put()
	db.run_in_transaction(txn)

def increment_day(name, count=None):
	stamp = int(datetime.now().strftime('%Y%m%d'))
	
	def txn():
		index = random.randint(0, 99)
		shard_name = name + str(stamp) + str(index)
		counter = StatisticsShard.get_by_key_name(shard_name)
		if counter is None:
			counter = StatisticsShard(key_name=shard_name, name=name, stamp=stamp)
		counter.count += (count if count else 1)
		counter.put()
	db.run_in_transaction(txn)
	
def maximum(name, count):
	stamp = int(datetime.now().strftime('%Y%m%d%H'))
	def txn():
		index = random.randint(0, 99)
		shard_name = name + str(stamp) + str(index)
		counter = StatisticsShard.get_by_key_name(shard_name)
		if counter is None:
			counter = StatisticsShard(key_name=shard_name, name=name, stamp=stamp)
		counter.count = max(counter.count, count)
		counter.put()
	db.run_in_transaction(txn)

def ratioStatistics(stamp):
	hour = replace_timezone(datetime.now(), 'Asia/Seoul').hour
	
	if APP_DEBUG==False and (hour<2 or hour>4):
		return True
	
	ratioStatistics = RatioStatistics.query(RatioStatistics.stamp==stamp).get()
	if ratioStatistics is None:
		ratioStatistics = RatioStatistics(stamp=stamp, step=0)
	
	while ratioStatistics.step<7:
		if ratioStatistics.step==0:
			ratioStatistics.user_games=[]
			ratioStatistics.user_games.append(User.query(User.accrue_record.play==1).count(read_policy=ndb.EVENTUAL_CONSISTENCY))
		elif ratioStatistics.step==1:
			ratioStatistics.user_games.append(User.query(User.accrue_record.play>1).count(read_policy=ndb.EVENTUAL_CONSISTENCY))
		elif ratioStatistics.step==2:
			ratioStatistics.user_games.append(User.query(User.accrue_record.play>2).count(read_policy=ndb.EVENTUAL_CONSISTENCY))
		elif ratioStatistics.step==3:
			ratioStatistics.user_games.append(User.query(User.accrue_record.play>10).count(read_policy=ndb.EVENTUAL_CONSISTENCY))
		elif ratioStatistics.step==4:
			ratioStatistics.user_games.append(User.query(User.accrue_record.play>50).count(read_policy=ndb.EVENTUAL_CONSISTENCY))
		elif ratioStatistics.step==5:
			ratioStatistics.user_games.append(User.query(User.accrue_record.play>200).count(read_policy=ndb.EVENTUAL_CONSISTENCY))
		elif ratioStatistics.step==6:
			ratioStatistics.user_count=User.query().count(read_policy=ndb.EVENTUAL_CONSISTENCY)
		ratioStatistics.step += 1
		ratioStatistics.put()
	return True

def accrueStatistics(stamp, stampUTC):
	def getIterCount(stat_format,platform,iterRange):
		return [getCount(stat_format.format(platform, i), stampUTC) for i in iterRange]
	
	def getIterCounts(stat_format,platform,iterRange1,iterRange2):
		y = []
		for i in iterRange1:
			x = []
			for j in iterRange2:
				x.append(getCount(stat_format.format(platform, i, j), stampUTC))
			y.append(x)
		return y
	
	def xsum(a, b):
		return (a if a else 0) + (b if b else 0)
	
	def xsums(list1, list2):
		if list1 is None:
			list1 = []
		if list2 is None:
			list2 = []
		count = max(len(list1), len(list2))
		result = []
		for i in range(count):
			result.append((list1[i] if i<len(list1) else 0) + (list2[i] if i<len(list2) else 0))
		return result
	
	def xxsums(usedItems, array2):
		if usedItems is None:
			usedItems = []
		while len(usedItems)<GAME_COUNT:
			usedItems.append(UsedItem())
		result = []
		for i in range(GAME_COUNT):
			item = UsedItem()
			item.item0 = usedItems[i].item0+array2[i][0]
			item.item1 = usedItems[i].item1+array2[i][1]
			item.item2 = usedItems[i].item2+array2[i][2]
			item.item3 = usedItems[i].item3+array2[i][3]
			result.append(item)
		return result
	
	MAX_STEP = 15
	
	s = Statistics.query(Statistics.stamp==stamp).get()
	if s is None:
		s = Statistics(stamp=stamp, step=0)
	p = Statistics.query(Statistics.step==MAX_STEP, Statistics.stamp<stamp).order(-Statistics.stamp).get()
	if p is None:
		p = Statistics(stamp=stamp-1, step=0)
	
	GAME_RANGE = range(GAME_COUNT)
	ITEM_RANGE = range(MAX_ITEMS)
	CHAR_RANGE = range(1, len(getStorageData("characters"))+1)
	CASH_RANGE = range(1, len(getStorageData("cashes"))+1)
	COIN_RANGE = range(1, len(getStorageData("coins"))+1)
	BOUT_RANGE = range(1, len(getStorageData("bouts"))+1)
	FEED_RANGE = range(FEED_SLOTS)
	REWARD_RANGE = range(REWARD_CASH, REWARD_MAX)
	
	while s.step<MAX_STEP:
		if s.step==0:
			s.i_register=xsum(p.i_register,getCount(STAT_REGISTER.format(PLATFORM_IOS), stampUTC))
			s.p_register=xsum(p.p_register,getCount(STAT_REGISTER.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_login=xsum(p.i_login,getCount(STAT_LOGIN.format(PLATFORM_IOS), stampUTC))
			s.p_login=xsum(p.p_login,getCount(STAT_LOGIN.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_dau=getCount(STAT_DAU.format(PLATFORM_IOS), int(stampUTC/100))
			s.p_dau=getCount(STAT_DAU.format(PLATFORM_PLAYSTORE), int(stampUTC/100))
		elif s.step==1:
			s.i_buy_cashes=xsums(p.i_buy_cashes,getIterCount(STAT_BUY_CASH,PLATFORM_IOS,CASH_RANGE))
			s.p_buy_cashes=xsums(p.p_buy_cashes,getIterCount(STAT_BUY_CASH,PLATFORM_PLAYSTORE,CASH_RANGE))
			s.i_present_cashes=xsums(p.i_present_cashes,getIterCount(STAT_PRESENT_CASH,PLATFORM_IOS,CASH_RANGE))
			s.p_present_cashes=xsums(p.p_present_cashes,getIterCount(STAT_PRESENT_CASH,PLATFORM_PLAYSTORE,CASH_RANGE))
		elif s.step==2:
			s.i_buy_coins=xsums(p.i_buy_coins,getIterCount(STAT_BUY_COIN,PLATFORM_IOS,COIN_RANGE))
			s.p_buy_coins=xsums(p.p_buy_coins,getIterCount(STAT_BUY_COIN,PLATFORM_PLAYSTORE,COIN_RANGE))
			s.i_buy_bouts=xsums(p.i_buy_bouts,getIterCount(STAT_BUY_BOUT,PLATFORM_IOS,BOUT_RANGE))
			s.p_buy_bouts=xsums(p.p_buy_bouts,getIterCount(STAT_BUY_BOUT,PLATFORM_PLAYSTORE,BOUT_RANGE))
		elif s.step==3:
			s.i_boast=xsum(p.i_boast,getCount(STAT_BOAST.format(PLATFORM_IOS), stampUTC))
			s.p_boast=xsum(p.p_boast,getCount(STAT_BOAST.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_send_bout=xsum(p.i_send_bout,getCount(STAT_SEND_BOUT.format(PLATFORM_IOS), stampUTC))
			s.p_send_bout=xsum(p.p_send_bout,getCount(STAT_SEND_BOUT.format(PLATFORM_PLAYSTORE), stampUTC))
		elif s.step==4:
			s.i_req_bout=xsum(p.i_req_bout,getCount(STAT_REQ_BOUT.format(PLATFORM_IOS), stampUTC))
			s.p_req_bout=xsum(p.p_req_bout,getCount(STAT_REQ_BOUT.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_invite=xsum(p.i_invite,getCount(STAT_INVITE.format(PLATFORM_IOS), stampUTC))
			s.p_invite=xsum(p.p_invite,getCount(STAT_INVITE.format(PLATFORM_PLAYSTORE), stampUTC))
		elif s.step==5:
			s.i_buy_chars=xsums(p.i_buy_chars,getIterCount(STAT_BUY_CHAR,PLATFORM_IOS,CHAR_RANGE))
			s.p_buy_chars=xsums(p.p_buy_chars,getIterCount(STAT_BUY_CHAR,PLATFORM_PLAYSTORE,CHAR_RANGE))
		elif s.step==6:
			s.i_adopt_chars=xsums(p.i_adopt_chars,getIterCount(STAT_ADOPT_CHAR,PLATFORM_IOS,CHAR_RANGE))
			s.p_adopt_chars=xsums(p.p_adopt_chars,getIterCount(STAT_ADOPT_CHAR,PLATFORM_PLAYSTORE,CHAR_RANGE))
		elif s.step==7:
			s.i_feed_self_chars=xsums(p.i_feed_self_chars,getIterCount(STAT_FEED_SELF_CHAR,PLATFORM_IOS,FEED_RANGE))
			s.p_feed_self_chars=xsums(p.p_feed_self_chars,getIterCount(STAT_FEED_SELF_CHAR,PLATFORM_PLAYSTORE,FEED_RANGE))
		elif s.step==8:
			s.i_feed_friend_chars=xsums(p.i_feed_friend_chars,getIterCount(STAT_FEED_FRIEND_CHAR,PLATFORM_IOS,FEED_RANGE))
			s.p_feed_friend_chars=xsums(p.p_feed_friend_chars,getIterCount(STAT_FEED_FRIEND_CHAR,PLATFORM_PLAYSTORE,FEED_RANGE))
		elif s.step==9:
			s.i_buff=xsums(p.i_buff,getIterCount(STAT_BUFF,PLATFORM_IOS,CHAR_RANGE))
			s.p_buff=xsums(p.p_buff,getIterCount(STAT_BUFF,PLATFORM_PLAYSTORE,CHAR_RANGE))
		elif s.step==10:
			s.i_clear_cooltime=xsum(p.i_clear_cooltime,getCount(STAT_CLEAR_COOLTIME.format(PLATFORM_IOS), stampUTC))
			s.p_clear_cooltime=xsum(p.p_clear_cooltime,getCount(STAT_CLEAR_COOLTIME.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_gacha_item=xsum(p.i_gacha_item,getCount(STAT_GACHA_ITEM.format(PLATFORM_IOS), stampUTC))
			s.p_gacha_item=xsum(p.p_gacha_item,getCount(STAT_GACHA_ITEM.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_use_dpoint=xsum(p.i_use_dpoint,getCount(STAT_USE_DAILYPOINT.format(PLATFORM_IOS), stampUTC))
			s.p_use_dpoint=xsum(p.p_use_dpoint,getCount(STAT_USE_DAILYPOINT.format(PLATFORM_PLAYSTORE), stampUTC))
		elif s.step==11:
			s.i_use_items=xxsums(p.i_use_items,getIterCounts(STAT_USE_ITEM,PLATFORM_IOS,GAME_RANGE,ITEM_RANGE))
			s.p_use_items=xxsums(p.p_use_items,getIterCounts(STAT_USE_ITEM,PLATFORM_PLAYSTORE,GAME_RANGE,ITEM_RANGE))
		elif s.step==12:
			s.i_game_counts=xsums(p.i_game_counts,getIterCount(STAT_GAME,PLATFORM_IOS,GAME_RANGE))
			s.p_game_counts=xsums(p.p_game_counts,getIterCount(STAT_GAME,PLATFORM_PLAYSTORE,GAME_RANGE))
		elif s.step==13:
			s.i_continue=xsum(p.i_continue,getCount(STAT_CONTINUE.format(PLATFORM_IOS), stampUTC))
			s.p_continue=xsum(p.p_continue,getCount(STAT_CONTINUE.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_unregister=xsum(p.i_unregister,getCount(STAT_UNREGISTER.format(PLATFORM_IOS), stampUTC))
			s.p_unregister=xsum(p.p_unregister,getCount(STAT_UNREGISTER.format(PLATFORM_PLAYSTORE), stampUTC))
			s.i_review=xsum(p.i_review,getCount(STAT_REVIEW.format(PLATFORM_IOS), stampUTC))
			s.p_review=xsum(p.p_review,getCount(STAT_REVIEW.format(PLATFORM_PLAYSTORE), stampUTC))
		elif s.step==14:
			s.i_rewards=xsums(p.i_rewards,getIterCount(STAT_REWARDS,PLATFORM_IOS,REWARD_RANGE))
			s.p_rewards=xsums(p.p_rewards,getIterCount(STAT_REWARDS,PLATFORM_PLAYSTORE,REWARD_RANGE))
		# to append step, see also MAX_STEP
		s.step += 1
		s.put()
	
	return True

